<?php


class WebSocket
{
    private $socket;
    private $accept = [];
    private $hands = [];

    function __construct($host, $port, $max)
    {
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, TRUE);
        socket_bind($this->socket, $host, $port);
        socket_listen($this->socket, $max);
        error_log(json_encode($this->socket));
    }

    /**
     * 开始
     */
    public  function start()
    {
        while (true) {
            $clent = $this->accept;
            $clients[] = $this->socket;
            socket_select($clent, $write, $except, null);
            foreach ($clent as $val) {
                if ($val == $this->socket) {
                    $this->accept = socket_accept($val);
                    $arr = array_keys($this->accept);
                    $key = end($arr);
                    $this->hands[$key] = false;
                } else {
                    $len = socket_recv($val, $buffer, 204800, null);
                    $key = array_search($val, $this->accept);
                    if (!$this->hands[$key]) {
                        $this->handShack($val, $buffer, $key);
                    } else if ($len < 1) {
                        $this->close($val);
                    } else {
                        $data = $this->decode($buffer);
                        error_log("解码:" . $data);
                        $data = $this->encode($data);
                        error_log("编码:" . $data);
                        foreach ($this->accept as $tmp) {
                            socket_write($tmp, $data, strlen($data));
                        }
                    }
                }
            }
            sleep(1);
        }
    }

    /**
     * 首次和客户端链接
     * @param $sock
     * @param $data
     * @param $key
     */
    function handShack($sock, $data, $key)
    {
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $data, $match)) {
            $resp = base64_decode(sha1($match[1] . time(), true));
            $upgrade = "HTTP/1.1 101 Switching Protocol\r\n" .
                "Upgrade: websocket\r\n" .
                "Connection:Upgrade\r\n" .
                "Sec-WebSocket-Accept: " . $resp . "\r\n\r\n";
            socket_write($sock, $upgrade, strlen($upgrade));
            $this->hands[$key] = true;
        }
    }

    /**
     * 关闭连接
     * @param $sock
     */
    function close($sock)
    {
        $key = array_search($sock, $this->accept);
        socket_close($sock);
        unset($this->accept[$key]);
        unset($this->hands[$key]);
    }

    /**
     * 字符解码
     * @param $buffer
     * @return string|null
     */
    function decode($buffer)
    {
        $len = $masks = $data = $decoded = null;
        $len = ord($buffer[1]) & 127;
        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    /** 字符编码
     * @param $buffer
     * @return string
     */
    function encode($buffer)
    {
        $length = strlen($buffer);
        if ($length <= 125) {
            return "\x81" . chr($length) . $buffer;
        } else if ($length <= 65535) {
            return "\x81" . chr(126) . pack("n", $length) . $buffer;
        } else {
            return "\x81" . chr(127) . pack("xxxxN", $length) . $buffer;
        }
    }
}